注意:原文发表时间是13年,所以实现有可能与新版不一致.
原文地址:https://www.elastic.co/cn/blog/customizing-your-document-routing
什么是路由
您的所有数据都存在于集群中某个位置的主分片中。您可能有五个分片或五百个,但任何特定的文档都只位于其中一个。路由是确定文档将保存到哪个分片中的过程。
因为Elasticsearch努力让90%的用户使用默认值,所以路由是自动处理的。对于大多数用户来说,文档存储在哪里并不重要。
默认的路由方案对文档的ID进行hash,并使用它来查找分片。这包括用户提供的ID和Elasticsearch随机生成的ID。默认路由使文档在整个分片集中均匀分布 - 不会出现文档集中倾斜到某个分片上形成热点。
自定义路由试用的场景
随机路由在大多数情况下都运行良好,但在某些情况下,可以通过自定义路由带来更好的性能。想象一个包含 20 个分片的索引(过度分配以支持未来的增长)。
当在集群上执行搜索请求时会发生什么?
- 搜索请求命中节点
- 节点将此请求广播到索引中的每个分片(主分片或副本分片)
- 每个分片执行搜索查询并返回结果
- 结果在网关节点上合并、排序并返回给用户
Elasticsearch 不知道在哪里查找您的文档。所有文档都随机分布在您的集群中……因此 Elasticsearch 别无选择,只能将请求广播到所有 20 个分片。这是不可忽略的开销,并且可能会影响性能。
如果我们能够告诉 Elasticsearch 该文档位于哪个分片中,岂不是很好? 然后您只需搜索一个分片即可找到您需要的文档。
这正是自定义路由的作用。
您不需要盲目地向所有分片广播,而是告诉 Elasticsearch“嘿! 搜索这个分片上的数据! 一切都在那里,我保证!” 例如,您可以根据文档的 user_id 路由文档。或者他们的邮政编码。或者您的应用程序中经常搜索/过滤的任何内容。
路由确保具有相同路由值的所有文档都将定位到同一个分片,从而无需广播搜索。
自定义路由速度很快
自定义路由可确保仅查询一个分片。
如果您的问题适合自定义路由,那么这有可能显着提高性能。
无论索引中有 20 个还是 100 个分片,自定义路由都可以确保只查询保存数据的分片。
配置自定义路由
使用自定义路由时,每当索引、获取、删除或更新文档时提供路由值非常重要。
忘记路由值可能会导致文档在多个分片上建立索引。作为保护措施,可以配置 _routing 字段来创建所有 CRUD 操作所需的自定义路由值:
1 | PUT my-index-000002 |
原文已经过时了,所有没有翻译原文;
这里是翻译自官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-routing-field.html
自定义路由搜索
自定义路由可以减少搜索的影响。不必将搜索请求发送到索引中的所有分片,而是将请求仅发送到与特定路由值(或多个值)匹配的分片:
1 | GET my-index-000001/_search?routing=user1,user2 |
此搜索请求将仅在与 user1 和 user2 路由值关联的分片上执行。
注意事项
在使用自定义路由时,您应该注意以下几个问题。
Parent/Child/Grandchild schemes
出于性能原因,父/子映射可确保所有子分片都路由到与父分片相同的分片。在内部,Elasticsearch 将子级的路由值设置为等于父级的 ID,确保每个人都位于同一个分片。
但是,如果您添加第三层(孙子),父/子映射就会失败。查看这些示例文档:
1 | curl -XPUT localhost:9200/parentchild/product/Product001 -d '{...}' |
相当简单的嵌套关系。产品的子代被称为“供应商”,而这些产品则有自己的子代,称为“VendorDetails”。那么,为什么这不起作用呢?让我们看看派生的路由值:
Doc ID | Parent | Routing Value |
---|---|---|
Product001 | - | Product001 |
VendorABC | Product001 | Product001 |
LocationXYZ | VendorABC | VendorABC |
正如您所看到的,文档将自动使用其父级的ID进行路由。VendorABC使用Product001(到目前为止很好),但LocationXYZ使用VendorABC(坏)。这意味着数据没有被正确地并置。
解决方案很简单:告诉孙子(以及任何后续的曾孙)它的路由值应该是祖父母的ID。在内部,Elasticsearch将优先选择路由参数而不是父参数:
1 | curl -XPUT localhost:9200/parentchild/product/Product001 -d '{...}' |
Doc ID | Parent | Routing Value |
---|---|---|
Product001 | - | Product001 |
VendorABC | Product001 | Product001 |
LocationXYZ | VendorABC | Product001 |
热点
当Elasticsearch管理路由时,它可以确保所有碎片的分布相当均匀。然而,一旦您开始实现自己的自定义方案,就完全有可能失去这种一致性。假设您正在按userID进行路由。你的大多数用户都很小,只有少量的文档……但偶尔你会遇到一个拥有数百万的怪物用户。
自定义路由将确保MonsterUser的所有文档都指向一个shard,而不是在集群中均匀分布。这对性能有好处——搜索会很快执行。
但是,如果MonsterUser2和MonsterUser3也被分配到同一个碎片,会发生什么?现在,在一个碎片上有三个大用户,而其余的碎片只加载了少量。
情况不太好。这些类型的场景可以而且确实会发生。针对这些“数据热点”的最佳防御措施是手动识别大用户(或其他急需性能的用户),并将其拆分为自己的索引。然后,您可以设置一个别名,使分离对应用程序透明。
现在,您可以两全其美:自定义路由将大多数用户沙盒在一个碎片中,而大用户则被拆分为自己的索引和一组碎片。
You can check-out any time you like, but you can never leave!
一旦在索引文档时指定了自定义路由,无论何时更新或删除文档,都必须继续指定。您已经从Elasticsearch获得了一些控制权,如果您决定在某个时候停止使用自定义路由,则没有简单的方法可以恢复此控制权。唯一的方法是重新建立数据索引(不指定自定义路由)。
结论
自定义路由是一个强大的功能,可以在特定情况下提高性能。虽然默认路由对大多数人来说已经足够了,但有时您只需要对文档的放置进行更多的控制。